package com.xd.common.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xd.common.exception.XkdException;
import io.swagger.annotations.ApiModelProperty;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.ResponseEntity;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@SuppressWarnings({"unchecked"})
public class XkdUtil {
    private static final String salt = "XDJR_SECURITY";
    private static final Logger log = LoggerFactory.getLogger(XkdUtil.class);


    /**
     * 汉语中数字大写
     */
    private static final String[] CN_UPPER_NUMBER = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
    /**
     * 汉语中货币单位大写，这样的设计类似于占位符
     */
    private static final String[] CN_UPPER_MONETRAY_UNIT = {"分", "角", "元", "拾", "佰", "仟", "万", "拾", "佰", "仟", "亿", "拾",
            "佰", "仟", "兆", "拾", "佰", "仟"};
    /**
     * 特殊字符：整
     */
    private static final String CN_FULL = "整";
    /**
     * 特殊字符：负
     */
    private static final String CN_NEGATIVE = "负";
    /**
     * 金额的精度，默认值为2
     */
    private static final int MONEY_PRECISION = 2;
    /**
     * 特殊字符：零元整
     */
    private static final String CN_ZEOR_FULL = "零元" + CN_FULL;

    /**
     * 计算第二天凌晨与当前时间的时间差秒数
     * @param
     * @return java.lang.Long
     * @author shy
     * @date 2021/3/12 18:10
     */
    public static Long getNowToNextDaySeconds() {
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DAY_OF_YEAR, 1);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return (cal.getTimeInMillis() - System.currentTimeMillis()) / 1000;
    }


    //转换大写金额
    public static String number2CNMontrayUnit(BigDecimal numberOfMoney) {
        StringBuffer sb = new StringBuffer();
        // -1, 0, or 1 as the value of this BigDecimal is negative, zero, or
        // positive.
        int signum = numberOfMoney.signum();
        // 零元整的情况
        if (signum == 0) {
            return CN_ZEOR_FULL;
        }
        // 这里会进行金额的四舍五入
        @SuppressWarnings("deprecation")
        long number = numberOfMoney.movePointRight(MONEY_PRECISION).setScale(0, 4).abs().longValue();
        // 得到小数点后两位值
        long scale = number % 100;
        int numUnit = 0;
        int numIndex = 0;
        boolean getZero = false;
        // 判断最后两位数，一共有四中情况：00 = 0, 01 = 1, 10, 11
        if (!(scale > 0)) {
            numIndex = 2;
            number = number / 100;
            getZero = true;
        }
        if ((scale > 0) && (!(scale % 10 > 0))) {
            numIndex = 1;
            number = number / 10;
            getZero = true;
        }
        int zeroSize = 0;
        while (true) {
            if (number <= 0) {
                break;
            }
            // 每次获取到最后一个数
            numUnit = (int) (number % 10);
            if (numUnit > 0) {
                if ((numIndex == 9) && (zeroSize >= 3)) {
                    sb.insert(0, CN_UPPER_MONETRAY_UNIT[6]);
                }
                if ((numIndex == 13) && (zeroSize >= 3)) {
                    sb.insert(0, CN_UPPER_MONETRAY_UNIT[10]);
                }
                sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
                sb.insert(0, CN_UPPER_NUMBER[numUnit]);
                getZero = false;
                zeroSize = 0;
            } else {
                ++zeroSize;
                if (!(getZero)) {
                    sb.insert(0, CN_UPPER_NUMBER[numUnit]);
                }
                if (numIndex == 2) {
                    if (number > 0) {
                        sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
                    }
                } else if (((numIndex - 2) % 4 == 0) && (number % 1000 > 0)) {
                    sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
                }
                getZero = true;
            }
            // 让number每次都去掉最后一个数
            number = number / 10;
            ++numIndex;
        }
        // 如果signum == -1，则说明输入的数字为负数，就在最前面追加特殊字符：负
        if (signum == -1) {
            sb.insert(0, CN_NEGATIVE);
        }
        // 输入的数字小数点后两位为"00"的情况，则要在最后追加特殊字符：整
        if (!(scale > 0)) {
            sb.append(CN_FULL);
        }
        return sb.toString();
    }

    /**
     * 通过身份证获取年龄
     */
    public static int getAgeByIdCard(String idCard) {
        if (idCard.length() != 18 && idCard.length() != 15) {
            throw new IllegalArgumentException("身份证号长度错误");
        }
        String year;
        String monthDay;
        if (idCard.length() == 18) {
            year = idCard.substring(6, 10);
            monthDay = idCard.substring(10, 14);
        } else {
            year = "19" + idCard.substring(6, 8);
            monthDay = idCard.substring(8, 12);
        }
        //获取当前时间字符串如：2022-1128
        String nowTimeStr = new SimpleDateFormat("yyyy-MMdd").format(new Date());
        String yearNow = nowTimeStr.substring(0, 4);// 当前年份
        String monthDayNow = nowTimeStr.substring(5, 9);// 当前月日
        int age = Integer.parseInt(yearNow) - Integer.parseInt(year);
        //age减一的情况 ：用户月日大于当前月日（开头可以为0的4位数int）
        if (Integer.parseInt(monthDay) > Integer.parseInt(monthDayNow)) {
            age = age - 1;
        }
        return age;
    }

    //限制一个身份证一个月内只能用一次
    public static void currentLimitingIdCard(String idCard, Integer maxLoginCount, RuntimeException e) {
        RedisTemplate<String, Object> redisTemplate2 = (RedisTemplate<String, Object>) BeanUtils.getBean("redisTemplate");
        //获取传进来的身份证，在缓存里查询
        Integer integer = (Integer) redisTemplate2.opsForValue().get(idCard);
        //如果查出来是空证明是一个月内内第一次登记
        Integer count = 1;
        //Integer loginCount=1; //登记次数
        if (integer == null) {
            //24小时过期时间
            redisTemplate2.opsForValue().set(idCard, count, 24 * 60 * 60 * 30, TimeUnit.SECONDS);
        } else if (integer < maxLoginCount) {//一个月内只能登记次数限制内
            //如果不为空则代表一个月内已经申请过至少一次，先获取过期时间
            Long expire = redisTemplate2.getExpire(idCard);
            //次数加一
            integer++;
            redisTemplate2.opsForValue().set(idCard, integer, expire, TimeUnit.SECONDS);

        } else {
            throw e;
        }
    }

    /**
     * 判断实体类中指定属性是否全为空，全为空时返回true,其他false
     *
     * @param entity
     * @param fieldNames
     * @return
     */
    public static boolean areFieldsEmpty(Object entity, String... fieldNames) {
        for (String fieldName : fieldNames) {
            try {
                Field field = entity.getClass().getDeclaredField(fieldName);
                field.setAccessible(true);
                Object value = field.get(entity);
                if (value != null) {
                    return false;
                }
            } catch (NoSuchFieldException | IllegalAccessException e) {
                // 处理异常情况
                e.printStackTrace();
            }
        }
        return true;
    }

    public static int counts(Object entity) {
        Class<?> clazz = entity.getClass();
        Field[] fields = clazz.getDeclaredFields();
        int count = 0;

        for (Field field : fields) {
            field.setAccessible(true);
            Object value = null;
            try {
                value = field.get(entity);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            if (value == null) {
                count++;
            }
        }

        return count;
    }

    /**
     * 去掉字符串首尾空格
     */
    public static void trim(Object entity, String... propertyNames) {
        try {
            for (String propertyName : propertyNames) {
                // 获取属性的 getter 方法名
                String getterName = "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
                // 获取属性的 setter 方法名
                String setterName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
                // 获取属性的类型
                Class<?> propertyType = entity.getClass().getDeclaredField(propertyName).getType();
                // 获取属性的 getter 方法
                Method getter = entity.getClass().getMethod(getterName);
                // 获取属性的 setter 方法
                Method setter = entity.getClass().getMethod(setterName, propertyType);
                // 获取属性值
                Object propertyValue = getter.invoke(entity);
                // 如果属性值不为 null 且为 String 类型，则去首尾空格并设置属性值
                if (propertyValue instanceof String) {
                    setter.invoke(entity, ((String) propertyValue).trim());
                }
            }
        } catch (Exception e) {
            // 异常处理
            e.printStackTrace();
        }
    }

    //从倒数第n个指定字符开始截取到最后
    public static String getLastStr(String inputString, char ch, int n) {
        // 计算字符 ch 在字符串 inputString 中出现的次数
        int count = 0;
        for (int i = inputString.length() - 1; i >= 0; i--) {
            if (inputString.charAt(i) == ch) {
                count++;
            }
            // 当字符 ch 在字符串 inputString 中出现的次数为 n 时，截取子字符串并返回
            if (count == n) {
                return inputString.substring(i);
            }
        }
        // 如果字符 ch 在字符串 inputString 中出现的次数少于 n 次，返回空串
        return "";
    }


    /**
     * 将对象转换为Map时跳过空属性值
     *
     * @param obj
     * @return
     */
    public static Map<String, String> convertToMap(Object obj) {
        Map<String, String> map = new HashMap<>();
        if (obj == null) return null;
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            String fieldName = field.getName();
            try {
                Object value = field.get(obj);
                if (ObjectUtils.isNotEmpty(value))
                    map.put(fieldName, value.toString());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }

    //使用使用org.apache.commons.beanutils进行转换,该方式可以把继承自父类的属性字段也进行赋值,靠谱
    public static Object mapToObject0(Map<String, Object> map, Class<?> beanClass) throws Exception {
        if (map == null)
            return null;

        Object obj = beanClass.newInstance();

        org.apache.commons.beanutils.BeanUtils.populate(obj, map);

        return obj;
    }

    public static Map<?, ?> objectToMap0(Object obj) {
        if (obj == null)
            return null;

        return new org.apache.commons.beanutils.BeanMap(obj);
    }


    //使用java的reflect进行转换,转换后的对象继承父类的字段没有正确赋值
    public static <T> T mapToObject(Map<String, Object> map, Class<T> beanClass) {
        T obj = null;
        if (map != null)
            try {
                obj = beanClass.newInstance();
                Field[] fields = obj.getClass().getDeclaredFields();
                for (Field field : fields) {
                    int mod = field.getModifiers();
                    if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
                        continue;
                    }
                    field.setAccessible(true);
//                    field.set(obj, map.get(field.getName()));
                    Object value = map.get(field.getName());
                    if (null == value) continue;
                    Class<?> aClass = value.getClass();
                    Class<?> fieldType = field.getType();

                    if (aClass.equals(fieldType))
                        field.set(obj, value);
                    else if ((aClass == Integer.class) && fieldType.equals(String.class)) {
                        field.set(obj, value.toString());
                    } else if ((fieldType == Integer.class) && aClass.equals(String.class)) {
                        field.set(obj, Integer.parseInt(value.toString()));
                    } else if ((fieldType == BigDecimal.class) && aClass.equals(String.class)) {
                        field.set(obj, new BigDecimal(value.toString()));
                    } else {
                        throw new XkdException("属性转换异常：" + field.getName());
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        return obj;
    }

    public static Map<String, Object> objectToMap(Object obj, boolean flag) {
        if (obj == null) {
            return null;
        }

        Map<String, Object> map = new HashMap<String, Object>();
        try {
            Field[] declaredFields = obj.getClass().getDeclaredFields();
            for (Field field : declaredFields) {
                field.setAccessible(true);
                Object value = field.get(obj);

                // 排除属性值为 null 的属性
                if (flag && value == null) continue;
                map.put(field.getName(), value);
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return map;
    }

    public static <T> T transferProperties(T source, String... properties) {
        T destination = null;
        try {
            Class<?> clazz = source.getClass();
            destination = (T) clazz.newInstance();
            for (String propertyName : properties) {
                Field field = clazz.getDeclaredField(propertyName);
                field.setAccessible(true);
                Object value = field.get(source);
                field.set(destination, value);
            }
        } catch (Exception e) {
            // 处理属性不存在的情况
            e.printStackTrace();
        }
        return destination;
    }


    public static void checkNonNullProperties(Object obj, String... propertyNames) {
        StringBuilder invalidProperties = new StringBuilder();
        StringBuilder emptyProperties = new StringBuilder();
        for (String propertyName : propertyNames) {
            try {
                // 使用反射获取属性值
                Method getterMethod = obj.getClass().getMethod("get" + capitalize(propertyName));
                Object propertyValue = getterMethod.invoke(obj);
                if (ObjectUtils.isEmpty(propertyValue)) {
                    if (emptyProperties.length() > 0) {
                        emptyProperties.append(", ");
                    }
                    String value = getAnnotationValue(obj, ApiModelProperty.class, propertyName);
                    emptyProperties.append(null == value ? propertyName : value);
                }
            } catch (NoSuchMethodException e) {
                if (invalidProperties.length() > 0) {
                    invalidProperties.append(", ");
                }
                String value = getAnnotationValue(obj, ApiModelProperty.class, propertyName);
                emptyProperties.append(null == value ? propertyName : value);
            } catch (Exception e) {
                // 处理其他反射异常
                e.printStackTrace();
            }
        }

        // 打印属性名不存在或属性值为空的信息
        StringBuilder message = new StringBuilder();
        if (invalidProperties.length() > 0) {
            message.append("以下参数为必传项: ").append(invalidProperties).append(",");
        }
        if (emptyProperties.length() > 0) {
            message.append("以下参数不能为空: ").append(emptyProperties).append(",");
        }
        if (message.length() > 0) {
            throw new XkdException(removeLastCharacter(message.toString()));
        }
    }

    //截取尾
    public static String removeLastCharacter(String str) {
        if (str == null || str.isEmpty()) {
            return str;
        }
        return str.substring(0, str.length() - 1);
    }

    //截取尾
    public static StringBuilder removeLastCharacter(StringBuilder str) {
        if (str == null || str.length() == 0) {
            return new StringBuilder();
        }
        return new StringBuilder(str.substring(0, str.length() - 1));
    }

    public static String manipulateString(String input, String prefix) {
        return input == null ? null : prefix + input.replaceAll(",(.*?)", "," + prefix + "$1");
    }

    public static int getNonNullFieldCount(Object obj, String... excludedFields) {
        int count = 0;

        // 获取对象的Class对象
        Class<?> clazz = obj.getClass();

        // 获取所有声明的字段（包括私有、受保护、公共字段）
        Field[] fields = clazz.getDeclaredFields();

        // 创建要排除的属性集合
        Set<String> excludedSet = new HashSet<>(Arrays.asList(excludedFields));

        // 遍历字段
        for (Field field : fields) {
            // 获取字段名
            String fieldName = field.getName();

            // 如果字段名存在于排除集合中，则跳过该字段
            if (excludedSet.contains(fieldName)) {
                continue;
            }

            // 设置字段可访问（如果是私有字段）
            field.setAccessible(true);

            try {
                // 获取字段的值
                Object value = field.get(obj);

                // 如果字段的值不为null，则计数加1
                if (value != null) {
                    count++;
                }
            } catch (IllegalAccessException e) {
                // 处理访问异常
                e.printStackTrace();
            }
        }

        return count;
    }

    public static String getNonNullFieldName(Object obj, String... excludedFields) {
        // 获取对象的Class对象
        Class<?> clazz = obj.getClass();

        // 获取所有声明的字段（包括私有、受保护、公共字段）
        Field[] fields = clazz.getDeclaredFields();

        // 创建要排除的属性集合
        Set<String> excludedSet = new HashSet<>(Arrays.asList(excludedFields));

        // 记录非空属性的名称
        String nonNullFieldName = null;

        // 遍历字段
        for (Field field : fields) {
            // 获取字段名
            String fieldName = field.getName();

            // 如果字段名存在于排除集合中，则跳过该字段
            if (excludedSet.contains(fieldName)) {
                continue;
            }

            // 设置字段可访问（如果是私有字段）
            field.setAccessible(true);

            // 获取字段的值
            Object value = null;
            try {
                value = field.get(obj);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

            // 如果字段的值不为null
            if (value != null) {
                // 如果之前已经找到了非空属性，则抛出异常
                if (nonNullFieldName != null) {
                    throw new XkdException("只能传入一个要更新的属性");
                }

                // 记录非空属性的名称
                nonNullFieldName = fieldName;
            }
        }

        // 如果非空属性数量为1，则返回该属性的名称
        if (nonNullFieldName != null) {
            return nonNullFieldName;
        } else {
            throw new XkdException("请传入要更新的属性");
        }
    }

    private static String capitalize(String str) {
        if (str == null || str.isEmpty()) {
            return str;
        }
        return str.substring(0, 1).toUpperCase() + str.substring(1);
    }


    private static String getAnnotationValue(Object entity, Class<? extends Annotation> annotationType, String propertyName) {
        Class<?> entityClass = entity.getClass();
        try {
            Field field = entityClass.getDeclaredField(propertyName);
            Annotation annotation = field.getAnnotation(annotationType);
            if (annotation != null) {
                // 获取注解的value值
                return annotationType.getMethod("value").invoke(annotation).toString();
            }
        } catch (NoSuchFieldException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Map<String, String> getAnnotatedFields(Class<?> entityClass, Class<? extends Annotation> annotationClass) {
        Map<String, String> annotatedFieldsMap = new HashMap<>();
        try {
            // 获取类中所有的字段
            Field[] fields = entityClass.getDeclaredFields();

            // 遍历字段，检查是否带有指定注解
            for (Field field : fields) {
                // 检查字段是否带有指定注解
                if (field.isAnnotationPresent(annotationClass)) {
                    // 获取注解实例
                    //MyAnnotation annotation = field.getAnnotation(MyAnnotation.class);
                    Annotation annotation = field.getAnnotation(annotationClass);
                    // 将属性名作为键，注解的值作为值存入Map
                    annotatedFieldsMap.put(field.getName(), annotationClass.getMethod("value").invoke(annotation).toString());
                }
            }
            return annotatedFieldsMap;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //获取localdata
    public static LocalDate getLocalDate(java.util.Date date) {
        //将参数转为系统默认的格式
        java.util.Date date1 = new Date(date.getTime());
        Instant instant = date1.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
        LocalDate localDate = localDateTime.toLocalDate();
        return localDate;
    }


    /*
     * 用于自定义API属性配置转换
     * 将嵌套json转换为map{自定义字段:映射字段}
     * */
    public static Map<String, Object> jsonToMap(String json) {
        return JSON.parseObject(json, new TypeReference<LinkedHashMap<String, Object>>() {
        }, Feature.OrderedField);
    }

    /*
     * 自定义属性赋值
     * 用于第三方自定义API调用
     * entry:享客达的客户线索类
     * customMap:自定义json串
     * */
    public static Map<String, Object> valueToMap(Object entry, Map<String, Object> customMap) {
        Map<String, Object> entryMap = BeanUtil.beanToMap(entry);
        Set<Map.Entry<String, Object>> m1 = customMap.entrySet();
        //循环拿到自定义每个字段的映射
        for (Map.Entry<String, Object> em1 : m1) {
            if (em1.getValue().getClass().equals(HashMap.class)) {
                Map<String, Object> value = (Map<String, Object>) em1.getValue();
                valueToMap(entry, value);
            }
            Set<Map.Entry<String, Object>> m2 = entryMap.entrySet();
            //循环比较映射字段，并赋值，如果没有找到第三方与本方对应的字段，该字段的值就用本身填写的默认值
            for (Map.Entry<String, Object> em2 : m2) {
                if (em2.getKey().equals(em1.getValue())) {
                    em1.setValue(em2.getValue());
                }
            }
        }
        return customMap;
    }


    @SafeVarargs
    public static <T> void move(Object sourceEntity, Collection<T> targetList, SFunction<T, ?>... selectedProperties) {
        try {
            for (T targetEntity : targetList) {
                for (SFunction<T, ?> selectedProperty : selectedProperties) {
                    String propertyName = getName(selectedProperty);
                    Field sourceField = getField(sourceEntity.getClass(), propertyName);
                    sourceField.setAccessible(true);

                    Field targetField = getField(targetEntity.getClass(), propertyName);
                    targetField.setAccessible(true);
                    targetField.set(targetEntity, sourceField.get(sourceEntity));
                }
            }
        } catch (IllegalAccessException | NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    @SafeVarargs
    public static <T> void move(Object sourceEntity, Collection<T> targetList, SFunction<T, ?> key, Object value, SFunction<T, ?>... selectedProperties) {
        try {
            for (T targetEntity : targetList) {
                for (SFunction<T, ?> selectedProperty : selectedProperties) {
                    String propertyName = getName(selectedProperty);
                    Field sourceField = getField(sourceEntity.getClass(), propertyName);
                    sourceField.setAccessible(true);//取出来源属性值

                    Field targetField = getField(targetEntity.getClass(), propertyName);
                    targetField.setAccessible(true);
                    targetField.set(targetEntity, sourceField.get(sourceEntity));//存入目标属性值
                }
                String keyName = getName(key);
                Field field = getField(targetEntity.getClass(), keyName);
                field.setAccessible(true);
                field.set(targetEntity, value);//设置定值
            }
        } catch (IllegalAccessException | NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    @SafeVarargs
    public static <T> void move(Object sourceEntity, T target, SFunction<T, ?>... selectedProperties) {
        try {
            for (SFunction<T, ?> selectedProperty : selectedProperties) {
                String propertyName = getName(selectedProperty);
                Field sourceField = getField(sourceEntity.getClass(), propertyName);
                sourceField.setAccessible(true);
                Field targetField = getField(target.getClass(), propertyName);
                targetField.setAccessible(true);
                Object value = sourceField.get(sourceEntity);
                targetField.set(target, value);
            }
        } catch (IllegalAccessException | NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    /**
     * 通过属性名获取属性对象（包括父类）
     *
     * @param clazz
     * @param columnName
     * @return
     * @throws NoSuchFieldException
     */
    private static Field getField(Class<?> clazz, String columnName) throws NoSuchFieldException {
        while (clazz != null) {
            try {
                return clazz.getDeclaredField(columnName);
            } catch (NoSuchFieldException e) {
                clazz = clazz.getSuperclass();
            }
        }
        throw new NoSuchFieldException("Field with column name '" + columnName + "' not found in the class hierarchy.");
    }

    private static <T> String getName(SFunction<T, ?> fn) {
        String implMethodName = LambdaUtils.resolve(fn).getImplMethodName();
        String fieldName = implMethodName.substring("get".length());
        fieldName = fieldName.replaceFirst(fieldName.charAt(0) + "", (fieldName.charAt(0) + "").toLowerCase());
        return fieldName;
    }


    public static <T> SFunction<T, ?> getPropertyFunction(Class<T> clazz, String propertyName) {
        try {
            String methodSuffix = Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
            Method method = clazz.getMethod("get" + methodSuffix);
            return (T t) -> {
                try {
                    return method.invoke(t);
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            };
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static <T> List<SFunction<T, ?>> getAllProperties(Class<T> clazz) {
        List<SFunction<T, ?>> properties = new ArrayList<>();
        Class<?> currentClass = clazz;
        while (currentClass != null) {
            for (Field field : currentClass.getDeclaredFields()) {
                String fieldName = field.getName();
                if (fieldName.equals("serialVersionUID")) continue;
                SFunction<T, ?> sFunction = getPropertyFunction(clazz, fieldName);
                if (sFunction != null) {
                    properties.add(sFunction);
                }
            }
            currentClass = currentClass.getSuperclass();
        }
        return properties;
    }

    /**
     * fastJson对象转换为字符串并格式化<br/>
     * <业务逻辑><br/>
     *
     * @param object
     * @return string 返回格式化后的json字符串
     * @author Jihua Yi
     */
    public static String jsonFormat(Object object) {
        return JSON.toJSONString(object, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat, SerializerFeature.WriteNullListAsEmpty);
    }

    /**
     * 获取嵌套json中的指定值，层级用”.“号隔开
     *
     * @param json
     * @param targetKey
     * @return
     */
    public static Object readJson(String json, String targetKey) {
        JsonNode rootNode = null;
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            rootNode = objectMapper.readTree(json);
        } catch (JsonProcessingException e) {//普通串
            return json;
        }
        targetKey = targetKey.replace(".", "/");
        JsonNode targetNode = rootNode.at("/" + targetKey);
        if (targetNode.isMissingNode())
            return null;
        return targetNode.isValueNode() ? targetNode.asText() : targetNode;
    }


    public static Object getValueFromNestedMap(Map<String, Object> data, String key) {
        String[] keys = key.split("\\.");
        Object value = data;
        for (String k : keys) {
            if (value instanceof Map) {
                value = ((Map<?, ?>) value).get(k);
            } else {
                return null;
            }
        }
        return value;
    }


    /**
     * 属性名转数据库字段名
     *
     * @param input
     * @return
     */
    public static String convertToSnakeCase(String input) {
        if (StrUtil.isBlank(input)) return null;
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < input.length(); i++) {
            char c = input.charAt(i);
            if (Character.isUpperCase(c)) {
                if (i > 0) {
                    result.append('_');
                }
                result.append(Character.toLowerCase(c));
            } else {
                result.append(c);
            }
        }
        return result.toString();
    }

    public static String convertTableNameToPropertyName(String tableName) {
        StringBuilder propertyName = new StringBuilder();

        boolean capitalizeNextChar = false;
        for (char c : tableName.toCharArray()) {
            if (c == '_') {
                capitalizeNextChar = true;
            } else if (capitalizeNextChar) {
                propertyName.append(Character.toUpperCase(c));
                capitalizeNextChar = false;
            } else {
                propertyName.append(Character.toLowerCase(c));
            }
        }

        return propertyName.toString();
    }

    public static String getNameByAnnotation(Class<?> clazz, Class<? extends Annotation> annotationClass) {
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields)
            if (field.isAnnotationPresent(annotationClass))
                return field.getName();
        return null;
    }

    /**
     * a/b=*%
     * 只保留整数位
     *
     * @param dividend
     * @param divisor  除数
     * @return
     */
    public static String percentage(Integer dividend, Integer divisor) {
        double result = (dividend.doubleValue() / divisor.doubleValue()) * 100;
        return Math.round(result) + "%";
    }

    public static Object getFieldByMethodName(Object entity, String methodName) {
        try {
            // 获取方法的 Method 对象，参数是方法名和参数类型
            Method method = entity.getClass().getDeclaredMethod(methodName);
            // 设置方法的访问权限为可访问
            method.setAccessible(true);
            // 调用私有方法
            return method.invoke(entity);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static Object getFieldByFieldName(Object entity, String fieldName) {
        try {
            // 获取方法的 Method 对象，参数是方法名和参数类型
            Field field = entity.getClass().getDeclaredField(fieldName);
            // 设置方法的访问权限为可访问
            field.setAccessible(true);
            // 调用私有方法
            return field.get(entity);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 设置null时的默认值
     *
     * @param obj
     * @param value
     * @param <T>
     * @return
     */
    public static <T> T ifNull(Object obj, T value) {
        return null == obj ? value : (T) obj;
    }

    public static <T> T ifNull(Object obj) {
        return null == obj ? null : (T) obj;
    }

    public static String ifNullS(Object obj) {
        return null == obj ? null : obj.toString();
    }

    /**
     * 随机数生成
     *
     * @param characters 范围
     * @param length     长度
     * @return
     */
    public static String generateRandomString(String characters, int length) {
        if (length <= 0)
            return "";

        Random random = new Random();
        StringBuilder stringBuilder = new StringBuilder(length);

        for (int i = 0; i < length; i++) {
            int randomIndex = random.nextInt(characters.length());
            char randomChar = characters.charAt(randomIndex);
            stringBuilder.append(randomChar);
        }

        return stringBuilder.toString();
    }

    public static void extractAllBetweenBraces(String inputString) {
        // 使用正则表达式提取所有{}中的内容
        Pattern pattern = Pattern.compile("\\{(.*?)}");
        Matcher matcher = pattern.matcher(inputString);

        // 查找匹配
        while (matcher.find()) {
            // 提取每个匹配组中的内容（即大括号内的内容）
            String extractedContent = matcher.group(1);
            System.out.println("Extracted Content: " + extractedContent);
        }

        // 如果没有找到匹配，输出相应的提示信息
        if (!matcher.matches() && matcher.hitEnd()) {
            System.out.println("No match found.");
        }
    }

    /**
     * 1,3,2,0"排序后"0,1,2,3
     *
     * @param inputString
     */
    public static String sort(String inputString, String interval) {
        // 使用 Stream 对包含数字的字符串按照数字顺序排序
        return Arrays.stream(inputString.split(interval))
                .map(Integer::parseInt)
                .sorted()
                .map(java.lang.String::valueOf)
                .reduce((s1, s2) -> s1 + interval + s2)
                .orElse("");
    }

    public static String random(int max) {
        return random0(1, max) + "";
    }

    public static Integer random0(int min, int max) {
        if (min > max) throw new RuntimeException("不允许min > max");
        return new Random().nextInt(max - min + 1) + min;
    }

    public static String random(int min, int max) {
        return random0(min, max) + "";
    }


    /**
     * 是否为包含关系
     *
     * @param p 父级
     * @param c 子级
     * @return
     */
    public static boolean isSubset(String p, String c) {
        Integer[] ps = Convert.toIntArray(p);
        Integer[] cs = Convert.toIntArray(c);
        Set<Integer> set1 = new HashSet<>();
        Set<Integer> set2 = new HashSet<>();

        for (Integer num : ps) {
            set1.add(num);
        }

        for (Integer num : cs) {
            set2.add(num);
        }

        // 判断 set2 是否是 set1 的子集
        return set1.containsAll(set2);
    }

    public static boolean isSubset(List<Integer> ps, List<Integer> cs) {
        Set<Integer> set1 = new HashSet<>();
        Set<Integer> set2 = new HashSet<>();

        for (Integer num : ps) {
            set1.add(num);
        }

        for (Integer num : cs) {
            set2.add(num);
        }

        // 判断 set2 是否是 set1 的子集
        return set1.containsAll(set2);
    }

    /**
     * a是否包含b
     *
     * @param a
     * @param b
     * @return
     */
    public static boolean contains(String a, String b) {
        if (null == a) throw new RuntimeException("比较对象不能为空");
        if (null == b) return false;
        return a.contains(b);
    }


    /*
     * 相除保留4位数
     * 百分比保留2位数
     * */
    public static String percentageRound(int numerator, int denominator) {
        if (denominator == 0) {
            return "0%";
        }
        BigDecimal e = new BigDecimal(numerator);
        BigDecimal f = new BigDecimal(denominator);
//        MathContext mathContext = new MathContext(4, RoundingMode.HALF_UP);
        BigDecimal divide = e.divide(f, 4, RoundingMode.HALF_UP);


        NumberFormat percentInstance = NumberFormat.getPercentInstance();
        percentInstance.setMaximumFractionDigits(2);//设置百分比位数
        return percentInstance.format(divide.doubleValue());
    }

    /*
     * 保留两位小数
     * */
    public static BigDecimal percentageBigDecimal(BigDecimal numerator, BigDecimal denominator) {
        if (denominator.compareTo(BigDecimal.ZERO) == 0) {
            return new BigDecimal(BigInteger.ZERO);
        }
        return numerator.divide(denominator, 2, RoundingMode.HALF_UP);
    }


    public static <T> List<T> deduplicate(List<T> a, List<T> b) {
        if (CollectionUtil.isEmpty(a)) return b;
        if (CollectionUtil.isEmpty(b)) return a;
        // 将数组 a 转换为 Set，以便快速查找
        Set<T> setA = new HashSet<>(a);
        // 创建一个 List 存储去重后的结果
        List<T> resultList = new ArrayList<>();
        // 遍历数组 b，将不重复的元素添加到结果列表
        for (T item : b) {
            if (!setA.contains(item)) {
                resultList.add(item);
            }
        }
        // 将数组 a 的元素添加到结果列表
        resultList.addAll(a);
        // 将结果列表转换为数组
        return resultList;
    }

    public static Date getOneTime(Date date, Integer second) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.SECOND, second);
        return calendar.getTime();
    }

    /**
     * 根据排序字符串排序
     *
     * @param input
     * @param s-ortOrder
     * @return
     */
    public static String getOneByOrderStr(String input, String sortOrder, String defaultStr) {
        if (StringUtils.isBlank(input)) return defaultStr;
        String[] inputArray = input.split(",");
        if (inputArray.length == 1) return inputArray[0];
        String[] sortOrderArray = sortOrder.split(",");
        Arrays.sort(inputArray, Comparator.comparingInt(num -> {
            String str = java.lang.String.valueOf(num);
            int index = Arrays.asList(sortOrderArray).indexOf(str);
            return index != -1 ? index : Integer.MAX_VALUE;
        }));
        return inputArray[0];
    }

    /*
     * 获取签名
     * */
    public static String getSign(Object obj) throws IllegalAccessException {
        TreeMap<String, Object> treeMap = new TreeMap<>();

        if (obj instanceof Map) {
            // 如果传入的是 Map
            Map<?, ?> map = (Map<?, ?>) obj;
            for (Map.Entry<?, ?> entry : map.entrySet()) {
                if (entry.getKey().toString().equals("serialVersionUID") || entry.getKey().toString().equals("sign") ||
                        entry.getValue() == null ||
                        StringUtils.isBlank(entry.getValue().toString()))
                    continue;
                if (entry.getValue() instanceof Collection && ((Collection<?>) entry.getValue()).isEmpty()) {
                    continue;
                }
                treeMap.put(entry.getKey().toString(), entry.getValue());
            }
        } else {
            // 如果传入的是实体类对象
            Class<?> clazz = obj.getClass();
            while (clazz != null) {
                Field[] declaredFields = clazz.getDeclaredFields();
                for (Field field : declaredFields) {
                    // 排除静态字段和 serialVersionUID 字段
                    if (Modifier.isStatic(field.getModifiers()) || "serialVersionUID".equals(field.getName())) {
                        continue;
                    }

                    String name = field.getName();
                    boolean accessFlag = field.isAccessible();
                    field.setAccessible(true);
                    Object value = field.get(obj);
                    field.setAccessible(accessFlag);

                    if (value != null && !name.equals("sign")) {
                        // 如果是 List，将 List 转换为 JSON 字符串
                        if (value instanceof List) {
                            value = JSON.toJSONString(value);
                        } else if (value instanceof String) {
                            if (StringUtils.isBlank((String) value))
                                continue;
                            value = ((String) value).trim(); // 去除字符串首尾空格
                        }

                        treeMap.put(name, value);
                    }
                }
                clazz = clazz.getSuperclass();
            }
        }
        StringBuilder s = new StringBuilder();
        for (Map.Entry<String, Object> entry : treeMap.entrySet()) {
            s.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
        }

        // 去掉最后一个 &
        String s1 = s.substring(0, s.length() - 1) + salt;

        return MD5Util.MD5a32(s1);
    }


    /*
     * 认证签名
     * */
    public static boolean checkSign(Object o, String sign) throws IllegalAccessException {
        // 将对象 o 转换为 JSONObject
//        JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(o));
        // 从 JSONObject 中获取 timestamp 字段
//        Long timestamp = jsonObject.getLong("timestamp");
//        if (!isTimestampValid(timestamp)) {
//          throw new XkdException("请求已过期");
//        }
        String s2 = getSign(o);
        return StringUtils.equals(s2, sign);
    }


    /*
     * 判断时间是否在1分钟内
     * */
    public static boolean isTimestampValid(long clientTimestamp) {
        // 当前服务器时间（毫秒）
        long currentTimestamp = System.currentTimeMillis();
        // 5分钟的毫秒值
        long fiveMinutesMillis = 60 * 1000;
        // 允许范围：当前时间 ± 5分钟
        return Math.abs(currentTimestamp - clientTimestamp) <= fiveMinutesMillis;
    }

    public static String pickRandomString(String input) {
        String[] strings = input.split(",");
        Random random = new Random();
        int randomIndex = random.nextInt(strings.length);
        return strings[randomIndex];
    }

    public static String getMsg(Map<String, Object> map, String defaultStr, String... msg) {
        for (String m : msg) {
            Object o = map.get(m);
            if (null != o) return o.toString();
        }
        return defaultStr;
    }

    public static int improvedArrangement(int num, int multiple) {
        if (0 == num) return multiple;
        if (num % multiple == 0) {
            return num;
        } else {
            return ((num / multiple) + 1) * multiple;
        }
    }

    public static <T> T copyProperties(Object source, T target) {
        org.springframework.beans.BeanUtils.copyProperties(source, target);
        return target;
    }

    /**
     * 当前时间到指定时间的分钟数
     *
     * @param hour
     * @param minute
     * @param second
     * @return
     */
    public static long getMinute(int hour, int minute, int second) {
        // 获取当前时间
        LocalDateTime now = LocalDateTime.now();

        // 获取当天晚上23:59:00的时间
        LocalTime endOfDay = LocalTime.of(hour, minute, second);
        LocalDateTime endOfToday = now.toLocalDate().atTime(endOfDay);

        // 计算当前时间到当天晚上23:59:00的分钟数
        return ChronoUnit.MINUTES.between(now, endOfToday);
    }


}
